package com.dycong.common.network;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created by Dycong on 2017/10/29.
 */
public abstract class AbstractHttpClient {

    public static final String DEFAULT_ENCODING = "UTF-8";

    private static Logger logger = Logger.getLogger(AbstractHttpClient.class);

    protected Optional<RequestConfig> proxyConfig = GlobalHttpClientProxy.getProxyRequestConfig();
    protected String userAgent;


    public abstract String doPost(String url, String encoding) throws IOException;

    public abstract String doGet(String url, String encoding) throws IOException;

    public abstract String postJson(String url, String encoding, String json) throws IOException;

    public abstract String doPost(String url, String encoding, Map<String, String> headerMap, Map<String, String> parameters) throws IOException;

    public abstract String doGet(String url, String encoding, Map<String, String> headerMap) throws IOException;

    public String doGet(String url, String encoding, Map<String, String> headerMap, Map<String, String> parameters) throws IOException {
        url = changeGetUrl(url, parameters, encoding);
        return doGet(url, encoding, headerMap);
    }

    public String doGet(String url) throws IOException {
        return doGet(url, DEFAULT_ENCODING);
    }


    public String doPost(String url) throws IOException {
        return doPost(url, DEFAULT_ENCODING);
    }


    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    protected String executeRequestByDefault(HttpClient client, HttpEntityEnclosingRequestBase request, String encoding, Map<String, String> headerMap, Map<String, String> parameters) throws IOException {
        genRequestEntity(request, encoding, headerMap, parameters);
        HttpResponse response = client.execute(request);
        try {
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return EntityUtils.toString(response.getEntity(), encoding);
            } else {
                return response.getStatusLine().getStatusCode() + "";
            }
        } finally {
            HttpClientUtils.closeQuietly(response);
        }
    }

    protected HttpRequestBase genRequestEntity(
            HttpRequestBase request,
            String encoding,
            Map<String, String> headerMap,
            Map<String, String> paramMap
    ) {

        //set proxy if present
        proxyConfig.ifPresent(request::setConfig);

        request.setHeader("User-Agent", userAgent);
        if (headerMap != null) {
            for (String key : headerMap.keySet()) {
                String value = headerMap.get(key);
                request.setHeader(key, value);
            }
        }

        if (request instanceof HttpPost) {
            if (paramMap != null) {
                ((HttpPost) request).setEntity(mapToRequestEntity(paramMap, encoding));
            }
        }
        return request;
    }

    protected String execute(HttpClient client, HttpUriRequest request, String encoding) throws IOException {
        return execute(client, request, encoding, null);
    }


    protected String execute(HttpClient client, HttpUriRequest request, String encoding, Map<String, String> responseMap) throws IOException {
        HttpResponse response = client.execute(request);
        if (responseMap != null) {
            responseMap.clear();
            Arrays.asList(response.getAllHeaders())
                    .forEach(header -> responseMap.put(header.getName(), header.getValue())
                    );
        }
        HttpEntity entity = response.getEntity();
        try {
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return EntityUtils.toString(entity, encoding);
            } else if (response.getStatusLine().getStatusCode() == HttpStatus.SC_GATEWAY_TIMEOUT) {
                return "504";
            } else if (response.getStatusLine().getStatusCode() == HttpStatus.SC_BAD_GATEWAY) {
                return "502";
            }
            return EntityUtils.toString(entity, encoding);
        } finally {
            HttpClientUtils.closeQuietly(response);
        }
    }

    public static HttpEntity mapToRequestEntity(Map<String, String> map, String encoding) {
        List<NameValuePair> params = map.keySet().stream().map(key -> new BasicNameValuePair(key, map.get(key))).collect(Collectors.toList());
        try {
            // TODO: 2018/3/20 HTTP协议对中文自动进行UrlEncoded，解决可能出现的乱码问题  IMPORTANT
            HttpEntity entity = new UrlEncodedFormEntity(params, encoding);
            try {
                if (logger.isDebugEnabled()) {
                    logger.debug(IOUtils.toString(entity.getContent(), encoding));
                }
            } catch (IOException e) {
                logger.debug("", e);
            }
            return entity;
            //           ((HttpPost) request).setEntity(entity);
        } catch (UnsupportedEncodingException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }
    }


    public String changeGetUrl(String originalURl, Map<String, String> paramMap, String encoding) {
        if (paramMap == null)
            return originalURl;


        List<? extends NameValuePair> list =
                paramMap.entrySet().stream().map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())).collect(Collectors.toList());
        if (originalURl.contains("?")) {
            return originalURl + "&" + URLEncodedUtils.format(list, encoding);
        } else {
            return originalURl + "?" + URLEncodedUtils.format(list, encoding);
        }
    }


    public String postJson(String url, String encoding, Map<String, String> headerMap, String json) throws IOException {
        HttpPost post = new HttpPost(url);
        genRequestEntity(post, encoding, headerMap, null);
        post.setEntity(new StringEntity(json, encoding));
        post.addHeader("content-type", "application/json; charset=" + encoding);
        return execute(getHttpClient(), post, encoding);
    }


    abstract protected HttpClient getHttpClient();

}
